package com.mdp.workflow.biz.re.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdp.core.api.CacheHKVService;
import com.mdp.core.entity.LangTips;
import com.mdp.core.err.BizException;
import com.mdp.core.utils.CollectionUtils;
import com.mdp.workflow.api.FlowModelService;
import com.mdp.workflow.api.FlowProcdefService;
import com.mdp.workflow.biz.re.entity.Procdef;
import com.mdp.workflow.biz.re.mapper.ProcdefMapper;
import com.mdp.workflow.comm.dict.ModelConstants;
import com.mdp.workflow.comm.tools.FlowableUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static java.util.Collections.emptyList;

@Service("mdp.workflow.re.ProcdefService")
public class FlowProcdefServiceImpl   implements FlowProcdefService {
	static Logger logger =LoggerFactory.getLogger(FlowProcdefServiceImpl.class);

	@Resource
	private RepositoryService repositoryService;

	@Autowired
	ProcdefMapper procdefMapper;
	
	@Autowired
	CacheHKVService cacheHKVService;
	
	public void clearCache(String key){
		cacheHKVService.remove(FlowModelService.START_PROC_PRE_CACHE_KEY,key);
	}


	//激活模型
	public void activate(String procdefId) {
		ProcessDefinition definition=repositoryService.getProcessDefinition(procdefId);
		if(!definition.getTenantId().equals(FlowableUtils.getTenantId())){
			throw new BizException("branchId-err-activate","该流程属于%s发布的流程，您无权激活它",definition.getTenantId());
		}
		repositoryService.activateProcessDefinitionById(procdefId);

		clearCache(definition.getKey());
	}

	//挂起模型
	public void suspend(String procdefId) {
		ProcessDefinition definition=repositoryService.getProcessDefinition(procdefId);
		if(!definition.getTenantId().equals(FlowableUtils.getTenantId())){
			throw new BizException("branchId-err-suspend","该流程属于%s发布的流程，您无权挂起它",definition.getTenantId());
		}

		repositoryService.suspendProcessDefinitionById(procdefId);

		clearCache(definition.getKey());
	}



	@Override
	public ProcessDefinition getProcessDefinition(String id) {
		return repositoryService.getProcessDefinition(id);
	}

	@Override
	public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {
		return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();
	}

	@Override
	public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
		if (StrUtil.isEmpty(deploymentId)) {
			return null;
		}
		return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
	}

	@Override
	public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
		if (CollUtil.isEmpty(deploymentIds)) {
			return emptyList();
		}
		return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
	}

	@Override
	public ProcessDefinition getActiveProcessDefinition(String key) {
		return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult();
	}

	@Override
	public List<Deployment> getDeploymentList(Set<String> ids) {
		if (CollUtil.isEmpty(ids)) {
			return emptyList();
		}
		List<Deployment> list = new ArrayList<>(ids.size());
		for (String id : ids) {
			CollectionUtils.addIfNotNull(list, getDeployment(id));
		}
		return list;
	}

	@Override
	public Deployment getDeployment(String id) {
		if (StrUtil.isEmpty(id)) {
			return null;
		}
		return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
	}

	@Override
	public ProcessDefinition createProcessDefinition(Model model, byte[] bpmnBytes ) {
		// 创建 Deployment 部署
		Deployment deploy = repositoryService.createDeployment()
				.key(model.getKey()).name(model.getName()).category(model.getCategory())
				.addBytes(model.getKey() + ModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)
				.tenantId(FlowableUtils.getTenantId())
				.disableSchemaValidation() // 禁用 XML Schema 验证，因为有自定义的属性
				.deploy();

		// 设置 ProcessDefinition 的 category 分类
		ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
				.deploymentId(deploy.getId()).singleResult();
		repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
		// 注意 1，ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
		// 注意 2，目前该项目的设计上，需要保证 Model、Deployment、ProcessDefinition 使用相同的 key，保证关联性。
		//          否则，会导致 ProcessDefinition 的分页无法查询到。
		if (!Objects.equals(definition.getKey(), model.getKey())) {
			throw new BizException(LangTips.errMsg("procdef-key-not-mathc","流程定义的标识期望是(%s)，当前是(%s)，请修改 BPMN 流程图",model.getKey(),definition.getKey()));
 		}
		if (!Objects.equals(definition.getName(), model.getName())) {
 			throw new BizException(LangTips.errMsg("procdef-name-not-mathc","流程定义的名称期望是(%s)，当前是(%s)，请修改 BPMN 流程图",model.getName(),definition.getName()));

		}

		clearCache(definition.getKey());
		return definition;
	}


	@Override
	public BpmnModel getProcessDefinitionBpmnModel(String id) {
		return repositoryService.getBpmnModel(id);
	}

	@Override
	public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(String suspensionState) {
		// 拼接查询条件
		ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
		if (Objects.equals(SuspensionState.SUSPENDED.getStateCode()+"", suspensionState)) {
			query.suspended();
		} else if (Objects.equals(SuspensionState.ACTIVE.getStateCode()+"", suspensionState)) {
			query.active();
		}
		// 执行查询
		query.processDefinitionTenantId(FlowableUtils.getTenantId());
		return query.list();
	}
	@Override
	public void updateProcessDefinitionState(String id, String state) {
		String key=id.split(":")[0];
		// 激活
		if (Objects.equals(SuspensionState.ACTIVE.getStateCode()+"", state)) {
			repositoryService.activateProcessDefinitionById(id, false, new Date());

			clearCache(key);
			return;
		}
		// 挂起
		if (Objects.equals(SuspensionState.SUSPENDED.getStateCode()+"", state)) {
			// suspendProcessInstances = false，进行中的任务，不进行挂起。
			// 原因：只要新的流程不允许发起即可，老流程继续可以执行。
			repositoryService.suspendProcessDefinitionById(id, false, new Date());

			clearCache(key);
			return;
		}
 	}



	@Override
	public List<Map<String, Object>> selectListMapByWhere(IPage page, QueryWrapper<Procdef> qw, Map<String, Object> params) {
		return procdefMapper.selectListMapByWhere(page,qw,params);
	}
}

